﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace HexToRAM
{
    class Program
    {
        static int[] hexTable = new int[] { 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 
            0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        };

        static byte     W       =  0; // acumulador, registro de la cpu
        static short    PC      =  0; // contador de programa, registro de la cpu
        static int      stackPtr= -1; // los valores válidos serian de 0 a 11
        static int   [] stack   = new int   [12];   // la pila de hard (es circular)
        static ushort[] ROM     = new ushort[8192]; // la rom
        static byte  [,]RAM     = new byte  [4,128];// la ram en 4 bancos

        static bool Z
        {
            get
            {
                return false;
            }
            set
            {
                value = false;
            }
        }

        static bool C
        {
            get
            {
                return false;
            }
            set
            {
                bool foo = value;
            }
        }

        static int banco
        {
            // bit RP0=5 
            // bit RP1=6
            // 76543210 pos
            // 0XY00000 STATUS
            // bits de selección de banco..
            //         RP1  RP0
            // maskb = 0110 0000 
            // maskh =    6    0

            get
            {
                return (RAM[0, 3] & 0x60) >> 5;
            }
            set
            {
                if ((value < 0) || (value > 3))
                    throw new Exception("quisieron setear un banco de memoria inexistente");
                RAM[0, 3] |= (byte)(value << 5);
                RAM[1, 3] |= (byte)(value << 5);
                RAM[2, 3] |= (byte)(value << 5);
                RAM[3, 3] |= (byte)(value << 5);
            }
        }

        static int contPalabras = 0;

        static void Main (string [] args)
        {
            // 0-9
            Debug.Assert(hexTable['1'] == 1);
            Debug.Assert(hexTable['2'] == 2);
            Debug.Assert(hexTable['3'] == 3);
            Debug.Assert(hexTable['4'] == 4);
            Debug.Assert(hexTable['5'] == 5);
            Debug.Assert(hexTable['6'] == 6);
            Debug.Assert(hexTable['7'] == 7);
            Debug.Assert(hexTable['8'] == 8);
            Debug.Assert(hexTable['9'] == 9);
            Debug.Assert(hexTable['0'] == 0);
            // A-F
            Debug.Assert(hexTable['A'] == 10);
            Debug.Assert(hexTable['B'] == 11);
            Debug.Assert(hexTable['C'] == 12);
            Debug.Assert(hexTable['D'] == 13);
            Debug.Assert(hexTable['E'] == 14);
            Debug.Assert(hexTable['F'] == 15);
            // a-f
            Debug.Assert(hexTable['a'] == 10);
            Debug.Assert(hexTable['b'] == 11);
            Debug.Assert(hexTable['c'] == 12);
            Debug.Assert(hexTable['d'] == 13);
            Debug.Assert(hexTable['e'] == 14);
            Debug.Assert(hexTable['f'] == 15);

            using (StreamReader sr = File.OpenText(args[0]))
            {
                string s = string.Empty;
                while (null != (s = sr.ReadLine())) {
                    //Console.Write(cuenta++);
                    parseLin(s);
                }
            }

            ConsoleColor cc = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("** hay {0} palabras **", contPalabras);
            Console.ForegroundColor = cc;
            Console.WriteLine("PIC16F87XA ROM dump:");

            for (int i = 0; i < 8192; i++)
                Console.WriteLine ("{0:0000} {1}", i, traducir (ROM[i]));
        }

        static void parseLin (string s)
        {
            //  TODO:
            //      falta interpretar:
            //          1) direcciones
            //          2) recordtype

            int index = 0;

            if (s.Length < 11)
                throw new Exception("la cadena no puede contener menos de 11 caracteres");
            if (s[index] != ':')
                throw new Exception("falta caracter inicial ':'");
            index++;

            int byteCount = 0;
            for (int i = 0; i < 2; i++) {
                byteCount = (byteCount * 16) + hexTable[s[index]];
                index++;
            }

            if (byteCount == 0)
                return;

            int dirEnBytes = 0;
            for (int i = 0; i < 4; i++) {
                dirEnBytes = (dirEnBytes * 16) + hexTable[s[index]];
                index++;
            }
            int direccion = dirEnBytes / 2;

            if (dirEnBytes == 0x400E)
            {
                ConsoleColor cc = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("** config en 0x400E **" + s);
                Console.ForegroundColor = cc;

                return;
            }

            //Console.ForegroundColor = ConsoleColor.Red;
            //Console.WriteLine(dirEnBytes.ToString("X4"));
            //Console.ForegroundColor = ConsoleColor.Gray;

            int recordType = 0;
            for (int i = 0; i < 2; i++) {
                recordType = (recordType * 16) + hexTable[s[index]];
                index++;
            }

            //Console.ForegroundColor = ConsoleColor.Cyan;
            //Console.WriteLine(recordType.ToString("X2"));
            //Console.ForegroundColor = ConsoleColor.Gray;

            if (recordType != 0)
                return;

            // aca leo las palabras con un short, el formato es little endian
            for (int i = 0; i < byteCount / 2; i++)
            {
                int byte1 = 0;
                for (int j = 0; j < 2; j++)
                {
                    byte1 = (byte1 * 16) + hexTable[s[index]];
                    index++;
                }

                int byte2 = 0;
                for (int j = 0; j < 2; j++)
                {
                    byte2 = (byte2 * 16) + hexTable[s[index]];
                    index++;
                }

                int palabra = byte1 + (byte2 * 256); // <- little endian

                if (palabra > 0x3FFF)
                    throw new Exception("error, no entendiste nada ale");

                // cargo la ROM, con las posiciones verdaderas...
                ROM [direccion + i] = (ushort) palabra;
                
                // muestro lo que voy haciendo, pero no es la ROM verdadera..
                contPalabras ++;
                //Console.WriteLine ("{0,4} {1}", contPalabras-1, traducir(palabra));
            }
        }


        // http://en.wikipedia.org/wiki/PIC_microcontroller
        static string traducir(int palabra)
        {
            ////////////////////////////////
            // instrucciones miscelaneas  //
            ////////////////////////////////

            // 00 0000 0000 0000 NOP
            if (palabra == 0)
                return "NOP";

            // 00 0000 0000 1000 RETURN
            if (palabra == 8)
                return "RETURN";

            // 00 0000 0000 1001 RETFIE
            if (palabra == 9)
                return "RETFIE";

            // 00 0000 0110 0010 OPTION
            if (palabra == 0x0062)
                return "OPTION";

            // 00 0000 0110 0011 SLEEP Go into standby mode
            // TO: Time-out bit 
            //      1 = After power-up, CLRWDT instruction, or SLEEP instructio
            //      0 = A WDT time-out occurred 
            // PD: Power-down bit 
            //      1 = After power-up or by the CLRWDT instruction 
            //      0 = By execution of the SLEEP instruction 
            if (palabra == 0x0063)
                return "SLEEP";

            // 00 0000 0110 0100 CLRWDT
            if (palabra == 0x0064)
                return "CLRWDT";

            {
                // 00 0000 0110 01ff "TRIS f" Copy W to tri-state register (f = 1, 2 or 3)
                int MASK_TRIS = 0x0067;
                int MASK_TRIS_F = 0x0003;
                if ((palabra & MASK_TRIS) == palabra)
                    return string.Format("TRIS {0}", palabra & MASK_TRIS_F);
            }

            /////////////////////////////////////
            // Operaciones de la ALU: dest <- OP(f,W) //
            /////////////////////////////////////

            {
                // 00 0000 1fff ffff "MOVWF f" (f <- W)
                int MASK_MOVWF = 0x00FF;
                int MASK_MOVWF_F = 0x007F;
                if ((palabra & MASK_MOVWF) == palabra)
                    return string.Format("MOVWF {0}", palabra & MASK_MOVWF_F);
            }

            {
                // 00 0001 dfff ffff "CLR f,d" flags[Z], si dest<-0, CLRW, si dest<-1 CLRF f
                int MASK_CLR = 0x01FF;
                int MASK_CLR_D = 0x0080;
                int MASK_CLR_F = 0x007F;

                if ((palabra & MASK_CLR) == palabra)
                    return ((palabra & MASK_CLR_D) == 0) ?
                        "CLRW"
                        :
                        string.Format("CLRF {0}", palabra & MASK_CLR_F)
                    ;
            }

            {
                // 00 0010 dfff ffff "SUBWF f,d" flags=[C,Z] dest <- f-W (dest <- f+~W+1)
                int MASK_SUBWF = 0x02FF;
                int MASK_SUBWF_D = 0x0080;
                int MASK_SUBWF_F = 0x007F;

                if ((palabra & MASK_SUBWF) == palabra)
                    return ((palabra & MASK_SUBWF_D) == 0) ?
                        string.Format("SUBWF {0},W", palabra & MASK_SUBWF_F)
                        :
                        string.Format("SUBWF {0},F", palabra & MASK_SUBWF_F)
                    ;
            }

            {
                // 00 0011 dfff ffff "DECF f,d" flags=[Z] dest <- f-1
                int MASK_DECF = 0x03FF;
                int MASK_DECF_D = 0x0080;
                int MASK_DECF_F = 0x007F;

                if ((palabra & MASK_DECF) == palabra)
                    return ((palabra & MASK_DECF_D) == 0) ?
                        string.Format("DECF {0},W", palabra & MASK_DECF_F)
                        :
                        string.Format("DECF {0},F", palabra & MASK_DECF_F)
                    ;
            }

            {
                // 00 0100 dfff ffff "IORWF f,d" flags=[Z] dest <- f | W (Inclusive OR)
                int MASK_IORWF = 0x04FF;
                int MASK_IORWF_D = 0x0080;
                int MASK_IORWF_F = 0x007F;

                if ((palabra & MASK_IORWF) == palabra)
                    return ((palabra & MASK_IORWF_D) == 0) ?
                        string.Format("IORWF {0},W", palabra & MASK_IORWF_F)
                        :
                        string.Format("IORWF {0},F", palabra & MASK_IORWF_F)
                    ;
            }

            {
                // 00 0101 dfff ffff "ANDWF f,d" flags=[Z] dest <- f & W (And)
                int MASK_ANDWF = 0x05FF;
                int MASK_ANDWF_D = 0x0080;
                int MASK_ANDWF_F = 0x007F;

                if ((palabra & MASK_ANDWF) == palabra)
                    return ((palabra & MASK_ANDWF_D) == 0) ?
                        string.Format("ANDWF {0},W", palabra & MASK_ANDWF_F)
                        :
                        string.Format("ANDWF {0},F", palabra & MASK_ANDWF_F)
                    ;
            }

            {
                // 00 0110 dfff ffff "XORWF f,d" flags=[Z] dest <- f ^ W (XOR)
                int MASK_XORWF = 0x06FF;
                int MASK_XORWF_D = 0x0080;
                int MASK_XORWF_F = 0x007F;

                if ((palabra & MASK_XORWF) == palabra)
                    return ((palabra & MASK_XORWF_D) == 0) ?
                        string.Format("XORWF {0},W", palabra & MASK_XORWF_F)
                        :
                        string.Format("XORWF {0},F", palabra & MASK_XORWF_F)
                    ;
            }

            {
                // 00 0111 dfff ffff "ADDWF f,d" flags=[C,Z] dest <- f + W (SUMA)
                int MASK_ADDWF = 0x07FF;
                int MASK_ADDWF_D = 0x0080;
                int MASK_ADDWF_F = 0x007F;

                if ((palabra & MASK_ADDWF) == palabra)
                    return ((palabra & MASK_ADDWF_D) == 0) ?
                        string.Format("ADDWF {0},W", palabra & MASK_ADDWF_F)
                        :
                        string.Format("ADDWF {0},F", palabra & MASK_ADDWF_F)
                    ;
            }

            {
                // 00 1000 dfff ffff "MOVF f,d" flags=[Z] dest <- f
                int MASK_MOVF = 0x08FF;
                int MASK_MOVF_D = 0x0080;
                int MASK_MOVF_F = 0x007F;

                if ((palabra & MASK_MOVF) == palabra)
                    return ((palabra & MASK_MOVF_D) == 0) ?
                        string.Format("MOVF {0},W", palabra & MASK_MOVF_F)
                        :
                        string.Format("MOVF {0},F", palabra & MASK_MOVF_F)
                    ;
            }

            {
                // 00 1001 dfff ffff "COMF f,d" flags=[Z] dest <- ~f (bitwise complement (o sea NOT))
                int MASK_COMF = 0x09FF;
                int MASK_COMF_D = 0x0080;
                int MASK_COMF_F = 0x007F;

                if ((palabra & MASK_COMF) == palabra)
                    return ((palabra & MASK_COMF_D) == 0) ?
                        string.Format("COMF {0},W", palabra & MASK_COMF_F)
                        :
                        string.Format("COMF {0},F", palabra & MASK_COMF_F)
                    ;
            }

            {
                // 00 1010 dfff ffff "INCF f,d" flags=[Z] dest <- f+1 (incrementa en 1 y guarda en dest)
                int MASK_INCF = 0x0AFF;
                int MASK_INCF_D = 0x0080;
                int MASK_INCF_F = 0x007F;

                if ((palabra & MASK_INCF) == palabra)
                    return ((palabra & MASK_INCF_D) == 0) ?
                        string.Format("INCF {0},W", palabra & MASK_INCF_F)
                        :
                        string.Format("INCF {0},F", palabra & MASK_INCF_F)
                    ;
            }

            {
                // 00 1011 dfff ffff "DECFSZ f,d" (no flags) dest <- f−1 (then skip if zero)
                int MASK_DECFSZ = 0x0BFF;
                int MASK_DECFSZ_D = 0x0080;
                int MASK_DECFSZ_F = 0x007F;

                if ((palabra & MASK_DECFSZ) == palabra)
                    return ((palabra & MASK_DECFSZ_D) == 0) ?
                        string.Format("DECFSZ {0},W", palabra & MASK_DECFSZ_F)
                        :
                        string.Format("DECFSZ {0},F", palabra & MASK_DECFSZ_F)
                    ;
            }

            {
                // 00 1100 dfff ffff "RRF f,d" flags=[C] dest <- CARRY << 7 | f >> 1, rotate right through carry
                int MASK_RRF = 0x0CFF;
                int MASK_RRF_D = 0x0080;
                int MASK_RRF_F = 0x007F;

                if ((palabra & MASK_RRF) == palabra)
                    return ((palabra & MASK_RRF_D) == 0) ?
                        string.Format("RRF {0},W", palabra & MASK_RRF_F)
                        :
                        string.Format("RRF {0},F", palabra & MASK_RRF_F)
                    ;
            }

            {
                // 00 1101 dfff ffff "RLF f,d" flags=[C] dest <- f<<1 | CARRY, rotate left through carry
                int MASK_RLF = 0x0DFF;
                int MASK_RLF_D = 0x0080;
                int MASK_RLF_F = 0x007F;

                if ((palabra & MASK_RLF) == palabra)
                    return ((palabra & MASK_RLF_D) == 0) ?
                        string.Format("RLF {0},W", palabra & MASK_RLF_F)
                        :
                        string.Format("RLF {0},F", palabra & MASK_RLF_F)
                    ;
            }

            {
                // 00 1110 dfff ffff "SWAPF f,d" dest ? f<<4 | f>>4, swap nibbles
                int MASK_SWAPF = 0x0EFF;
                int MASK_SWAPF_D = 0x0080;
                int MASK_SWAPF_F = 0x007F;

                if ((palabra & MASK_SWAPF) == palabra)
                    return ((palabra & MASK_SWAPF_D) == 0) ?
                        string.Format("SWAPF {0},W", palabra & MASK_SWAPF_F)
                        :
                        string.Format("SWAPF {0},F", palabra & MASK_SWAPF_F)
                    ;
            }

            {
                // 00 1111 dfff ffff "INCFSZ f,d" dest <- f+1, then skip if zero
                int MASK_INCFSZ = 0x0FFF;
                int MASK_INCFSZ_D = 0x0080;
                int MASK_INCFSZ_F = 0x007F;

                if ((palabra & MASK_INCFSZ) == palabra)
                    return ((palabra & MASK_INCFSZ_D) == 0) ?
                        string.Format("INCFSZ {0},W", palabra & MASK_INCFSZ_F)
                        :
                        string.Format("INCFSZ {0},F", palabra & MASK_INCFSZ_F)
                    ;
            }

            /////////////////////////
            // Operaciones de bits //
            /////////////////////////

            {
                // 01 00bb bfff ffff "BCF f,b" Clear bit b of f
                int MASK_BCF = 0x13FF;
                int MASK_BCF_BIT = 0x0380;
                int MASK_BCF_F = 0x007F;

                if ((palabra & MASK_BCF) == palabra)
                    return string.Format(
                        "BCF {0},{1}",
                        palabra & MASK_BCF_F,
                        (palabra & MASK_BCF_BIT) >> 7
                    );
            }

            {
                // 01 01bb bfff ffff "BSF f,b" Set bit b of f
                int MASK_BSF = 0x17FF;
                int MASK_BSF_BIT = 0x380;
                int MASK_BSF_F = 0x007F;

                if ((palabra & MASK_BSF) == palabra)
                    return string.Format(
                        "BSF {0},{1}",
                        palabra & MASK_BSF_F,
                        (palabra & MASK_BSF_BIT) >> 7
                    );
            }

            {
                // 01 10bb bfff ffff "BTFSC f,b" Skip if bit b of f is clear
                int MASK_BTFSC = 0x1BFF;
                int MASK_BTFSC_BIT = 0x380;
                int MASK_BTFSC_F = 0x007F;

                if ((palabra & MASK_BTFSC) == palabra)
                    return string.Format(
                        "BTFSC {0},{1}",
                        palabra & MASK_BTFSC_F,
                        (palabra & MASK_BTFSC_BIT) >> 7
                    );
            }

            {
                // 01 11bb bfff ffff "BTFSS f,b" Skip if bit b of f is set
                int MASK_BTFSS = 0x1FFF;
                int MASK_BTFSS_BIT = 0x380;
                int MASK_BTFSS_F = 0x007F;

                if ((palabra & MASK_BTFSS) == palabra)
                    return string.Format(
                        "BTFSS {0},{1}",
                        palabra & MASK_BTFSS_F,
                        (palabra & MASK_BTFSS_BIT) >> 7
                    );
            }

            ///////////////////////
            // Control de flujo  //
            ///////////////////////

            {
                // 10 0kkk kkkk kkkk "CALL k" Call subroutine
                int MASK_CALL   = 0x27FF;
                int MASK_CALL_K = 0x07FF;

                if ((palabra & MASK_CALL) == palabra)
                    return string.Format(
                        "CALL {0}",
                        palabra & MASK_CALL_K
                    );
            }

            {
                // 10 1kkk kkkk kkkk "GOTO k" Jump to address k
                int MASK_GOTO = 0x2FFF;
                int MASK_GOTO_K = 0x07FF;

                if ((palabra & MASK_GOTO) == palabra)
                    return string.Format(
                        "GOTO {0}",
                        palabra & MASK_GOTO_K
                    );
            }

            //////////////////////////////////////////////////////////
            // Operaciones con W y literales de 8-bit: W <- OP(k,W) //
            //////////////////////////////////////////////////////////

            {
                // 11 00xx kkkk kkkk "MOVLW k" W <- k
                int MASK_MOVLW = 0x33FF;
                int MASK_MOVLW_K = 0x00FF;

                if ((palabra & MASK_MOVLW) == palabra)
                {
                    W = (byte)(palabra & MASK_MOVLW_K);
                    PC++;

                    return string.Format(
                        "MOVLW {0}",
                        palabra & MASK_MOVLW_K
                    );
                }
            }

            {
                // 11 01xx kkkk kkkk "RETLW k" W <- k, then return from subroutine
                int MASK_RETLW = 0x37FF;
                int MASK_RETLW_K = 0x00FF;

                if ((palabra & MASK_RETLW) == palabra)
                    return string.Format(
                        "RETLW {0}",
                        palabra & MASK_RETLW_K
                    );
            }

            {
                // 11 1000 kkkk kkkk "IORLW k" flags=[Z] W <- k | W, bitwise logical or
                int MASK_IORLW = 0x38FF;
                int MASK_IORLW_K = 0x00FF;

                if ((palabra & MASK_IORLW) == palabra)
                    return string.Format(
                        "IORLW {0}",
                        palabra & MASK_IORLW_K
                    );
            }

            {
                // 11 1001 kkkk kkkk "ANDLW k" flags=[Z] W <- k & W, bitwise and
                int MASK_ANDLW = 0x39FF;
                int MASK_ANDLW_K = 0x00FF;

                if ((palabra & MASK_ANDLW) == palabra)
                    return string.Format(
                        "ANDLW {0}",
                        palabra & MASK_ANDLW_K
                    );
            }

            {
                // 11 1010 kkkk kkkk "XORLW k" flags=[Z] W <- k ^ W, bitwise exclusive or
                int MASK_XORLW = 0x3AFF;
                int MASK_XORLW_K = 0x00FF;

                if ((palabra & MASK_XORLW) == palabra)
                    return string.Format(
                        "XORLW {0}",
                        palabra & MASK_XORLW_K
                    );
            }

            {
                // 11 1011 kkkk kkkk (reserved)
            }

            {
                // 11 110x kkkk kkkk "SUBLW k" flags=[C,Z] W <- k-W (dest <- k+~W+1)
                int MASK_SUBLW = 0x3DFF;
                int MASK_SUBLW_K = 0x00FF;

                if ((palabra & MASK_SUBLW) == palabra)
                    return string.Format(
                        "SUBLW {0}",
                        palabra & MASK_SUBLW_K
                    );
            }

            { 
                // 11 111x kkkk kkkk "ADDLW k" flags=[C,Z] W <- k+W
                int MASK_ADDLW = 0x3FFF;
                int MASK_ADDLW_K = 0x00FF;

                if ((palabra & MASK_ADDLW) == palabra)
                    return string.Format(
                        "ADDLW {0}",
                        palabra & MASK_ADDLW_K
                    );
            }

            throw new Exception(string.Format("la palabra {0} no existe boludooo...", palabra));
        }
    }
}
